/**
 * 参考preact 以及vue 
 */

/**
 * 暂理解为一个全局对象
 * preact生命周期钩子
 */
const options = {

}


let currentIndex = 0

let currentComponent = null

/**初始化一个空的function */
let afterPaint = () => {}

let afterPaintEffects = []


let oldBeforeRender = options._render
options._render = vnode => {
  if (oldBeforeRender) oldBeforeRender(vnode)

  currentComponent = vnode._component
  currentIndex = 0

  if (currentComponent._hooks) {
    currentComponent._hooks._pendingEffects = handleEffects(currentComponent._hooks._pendingEffects)
  }

}

/**初始化defer,利用requestAnimationFrame来实现event loop */
if (typeof window !== undefined) {
  afterPaint = component => {
    if (!component._afterPaintQueued && (component._afterPaintQueued = true) && afterPaintEffects.push(component)===1){
      /**当添加第一个的时候 */
      window.requestAnimationFrame(flushAfterPaintEffects)
    }

  }

}

function flushAfterPaintEffects(){
   afterPaintEffects.some(component=>{
     component._afterPaintQueued=false
     if(component._parentDom){
       component._hooks._pendingEffects=handleEffects(component._hooks._pendingEffects)
     }
   })
}


let oldAfterDiff = options.diffed
options.diffed = vnode => {
  if (oldAfterDiff) oldAfterDiff(vnode)

  const c = vnode._component
  if (!c) return

  const hooks = c._hooks
  if (hooks) {
    hooks._handles = bindHandles(hooks._handles)
    hooks._pendingLayoutEffects = handleEffects(hooks._pendingLayoutEffects)
  }


}


function bindHandles(handles) {
  handles.some(handle => {
    if (handle.ref) handle.ref.current = handle.createHandle()
  })
}


/**
 * 
 * @param {object array} effects 
 * 先执行上一次暂存的旧的cleanup回调，再将新的cleanup存入
 */
function handleEffects(effects) {

  if (!effects) return
  effects.forEach(invokeCleanup)
  effects.forEach(invokeEffect)
  return []

}


function invokeCleanup(hook) {
  if (hook._cleanup) hook._cleanup()
}


function invokeEffect(hook) {
  const result = hook._value() //callback function
  if (typeof result === 'function') hook.cleanup = result
}





/**
 * 
 * @param {number} index 
 */
function getHookState(index) {

  const hooks = currentComponent && currentComponent._hooks ||
    (currentComponent._hooks = {
      _list: [],
      _pendingEffects: [],
      _pendingLayoutEffects: [],
      _handles: [],
    })

  if (index >= hooks._list.length)
    hooks._list.push({})

  return hooks._list[index]

}


function useEffect(callback, args) {

  const state = getHookState(currentIndex++)
  if (argsChanged(state._args, args)) {
    state._value = callback
    state._args = args

    currentComponent._hooks._pendingEffects.push(state)
    afterPaint(state)

  }



}


function useLayoutEffect(callback, args) {

  const state = getHookState(currentIndex++)
  if (argsChanged(state._args, args)) {
    state._value = callback
    state._args = args

    currentComponent._hooks._pendingLayoutEffects.push(state)
  }

}

function useRef(initValue) {
  return useMemo(() => ({
    current: initValue
  }), [])
}


function useMemo(callback, args) {

  const state = getHookState(currentIndex++)
  if (argsChanged(state._args, args)) {
    state._args = args
    state._callback = callback // 为何还要再存_callback
    return state._value = callback()
  }

  return state._value

}

function useCallback(callback, args) {

  return useMemo(() => callback, args)

}

function useReducer(reducer, initState, init) {

  const hookState = getHookState(currentIndex++)

  if (!hookState._component) {
    hookState._component = currentComponent
    hooksState._value = [ //第一个参数state，第二个参数dispatch
      !init ? invokeOrReturn(null, initState) : init(initState),
      action => {
        const nextValue = reducer(hookState._value[0], action)
        if (hookState._value[0] !== nextValue) {
          hookState._value[0] = nextValue
          hookState._component.setState({})
        }
      }
    ]
  }

  return hookState._value

}

function useState(initState) {

  return useReducer(invokeOrReturn, initState)

}


function useContext(context) {

  const provider = currentComponent.context[context._id]
  if (!provider) return context._defaultValue
  const state = getHookState(currentIndex++)
  if (state._value == null) {
    state._value = true
    provider.sub(currentComponent)
  }

}




/**参数是否变化，旧参数不存在或者数组情况下，新参数与旧参数不一致 */
function argsChanged(olds, news) {
  return !olds || news.some((arg, index) => arg !== olds[index])
}

function invokeOrReturn(arg, f) {
  return typeof f === 'function' ? f(arg) : f
}